package com.shopnight.component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.shopnight.constant.WebSocketConstant;
import com.shopnight.entity.ChatRoom;
import com.shopnight.entity.ChatRoomInferior;
import com.shopnight.entity.User;
import com.shopnight.service.ChatRoomInferiorService;
import com.shopnight.service.ChatRoomService;
import com.shopnight.service.UserService;
import com.shopnight.socketVo.MessageVo;
import com.shopnight.socketVo.OnOpenVo;
import org.apache.commons.lang.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

@ServerEndpoint(value = "/websocket/{roomId}/{userId}")
@Component
public class WebSocket {
    private final static Logger logger = LogManager.getLogger(WebSocket.class);

    /**
     * 静态变量，用来记录当前在线连接数。应该把它设计成线程安全的
     */

    //private static int onlineCount;

    private static Map<Integer,Integer> onlineCountMap = new HashMap<>();

    /**
     * concurrent包的线程安全Map，用来存放每个客户端对应的MyWebSocket对象
     */
    private static ConcurrentHashMap<Integer, WebSocket> webSocketMap = new ConcurrentHashMap<>();

    /**
     * 与某个客户端的连接会话，需要通过它来给客户端发送数据
     */

    private Session session;
    private Integer userId;
    private Integer roomId;
    private String roomName;
    private String userNick;
    private String avatar;

    private static ChatRoomService chatRoomService;

    private static ChatRoomInferiorService chatRoomInferiorService;

    private static Executor executor;

    private static UserService userService;

    @Autowired
    public void setChatRoomService(ChatRoomService chatRoomService) {
        WebSocket.chatRoomService = chatRoomService;
    }

    @Autowired
    public void ThreadPoolTaskExecutor(@Qualifier("taskExecutor") Executor executor) {

        WebSocket.executor = executor;
    }

    @Autowired
    public void setChatRoomInferiorService(ChatRoomInferiorService chatRoomInferiorService) {
        WebSocket.chatRoomInferiorService = chatRoomInferiorService;
    }

    @Autowired
    public void setUser(UserService userService) {
        WebSocket.userService = userService;
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") Integer userId, @PathParam(value = "roomId") Integer roomId) {
        this.session = session;

        this.userId = userId;
        this.roomId=roomId;

        User user = userService.getById(userId.longValue());
        this.userNick=user.getUserNick();
        this.avatar=user.getAvatar();
        ChatRoom chatRoom = chatRoomService.getById(roomId);
        this.roomName=chatRoom.getRoomName();
        //加入map
        webSocketMap.put(userId, this);
        List<Integer> keysAsList = webSocketMap.keySet().stream().collect(Collectors.toList());
//        logger.info("用户{}({})连接{}聊天室成功,当前在线人数为{}",user.getUserNick(), userId, roomName, webSocketMap.size());
        OnOpenVo onOpenVo = new OnOpenVo();
        onOpenVo.setUserId(userId);
        onOpenVo.setUserNick(userNick);
        onOpenVo.setAvatar(avatar);
        onOpenVo.setRoomId(roomId);
        onOpenVo.setRoomName(roomName);
        onOpenVo.setOnlineCount(webSocketMap.size());
        onOpenVo.setUserIds(keysAsList);
        onOpenVo.setStatus(WebSocketConstant.StatusEnum.CONNECTION.getCode());

        for (Integer item : webSocketMap.keySet()) {
            try {
                String jsonString = JSON.toJSONString(onOpenVo);
                //String message="用户"+userNick+"({"+onOpenVo.getUserId()+"})"+"连接"+onOpenVo.getRoomName()+"("+onOpenVo.getRoomId()+"号)聊天室成功!当前在线人数为"+onOpenVo.getOnlineCount();
                webSocketMap.get(item).sendMessage(jsonString);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        //从map中删除
        webSocketMap.remove(userId);
        logger.info("用户{}({})关闭连接{}聊天室！当前在线人数为{}",userNick, userId, roomName,  webSocketMap.size());
        List<Integer> keysAsList = webSocketMap.keySet().stream().collect(Collectors.toList());
        OnOpenVo onOpenVo = new OnOpenVo();
        onOpenVo.setUserNick(userNick);
        onOpenVo.setAvatar(avatar);
        onOpenVo.setUserId(userId);
        onOpenVo.setRoomId(roomId);
        onOpenVo.setRoomName(roomName);
        onOpenVo.setOnlineCount(webSocketMap.size());
        onOpenVo.setUserIds(keysAsList);
        onOpenVo.setStatus(WebSocketConstant.StatusEnum.CLOSE.getCode());
        for (Integer item : webSocketMap.keySet()) {
            try {
                String jsonString = JSON.toJSONString(onOpenVo);
                //String message="用户"+userNick+"({"+userId+"})"+"断开"+roomName+"("+roomId+"号)聊天室!当前在线人数为"+webSocketMap.size();
                webSocketMap.get(item).sendMessage(jsonString);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.info("来自客户端用户：{} 消息:{}",userId, message);
        ChatRoomInferior chatRoomInferior = new ChatRoomInferior();

        CompletableFuture<Boolean> booleanCompletableFuture = CompletableFuture.supplyAsync(() -> {
                    chatRoomInferior.setUserId(userId);
                    chatRoomInferior.setRoomId(roomId);
                    JSONObject jsonObject = JSON.parseObject(message);
                    chatRoomInferior.setType(Integer.valueOf(jsonObject.get("type").toString()));
                    if (jsonObject.containsKey("fatherId"))
                        chatRoomInferior.setFatherId(Integer.valueOf(jsonObject.get("fatherId").toString()));
                    chatRoomInferior.setMessage(jsonObject.get("message").toString());
                    if (jsonObject.containsKey("duration"))
                        chatRoomInferior.setDuration(Long.valueOf(jsonObject.get("duration").toString()));
                    chatRoomInferior.setReleaseTime(new Date());
                   return   chatRoomInferiorService.save(chatRoomInferior);
               }, executor);


         booleanCompletableFuture.thenRunAsync(() -> {
            for (Integer item : webSocketMap.keySet()) {
                try {
                    MessageVo messageVo = new MessageVo();
                    messageVo.setId(chatRoomInferior.getId());
                    messageVo.setMessage(message);
                    messageVo.setType(chatRoomInferior.getType());
                    messageVo.setDuration(chatRoomInferior.getDuration());
                    messageVo.setFatherId(chatRoomInferior.getFatherId());
                    messageVo.setReleaseTime(chatRoomInferior.getReleaseTime());
                    messageVo.setAvatar(avatar);
                    messageVo.setUserId(userId);
                    messageVo.setUserNick(userNick);
                    messageVo.setRoomId(roomId);
                    MessageVo.fatherMessageVo fatherMessageVo = new MessageVo.fatherMessageVo();

                    if (messageVo.getFatherId() != null){
                        ChatRoomInferior chatRoomInferiorDb = chatRoomInferiorService.getById(messageVo.getFatherId());
                        BeanUtils.copyProperties(chatRoomInferiorDb, fatherMessageVo);
                        fatherMessageVo.setUserNick(userService.getById(fatherMessageVo.getUserId()).getUserNick());
                        messageVo.setFatherMessageVo(fatherMessageVo);
                    }
                    String jsonString = JSON.toJSONString(messageVo);
                    webSocketMap.get(item).sendMessage(jsonString);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }, executor);

        
    }

    /**
     * 发生错误时调用
     *
     * @OnError
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 向客户端发送消息
     */
    public void sendMessage(String message) throws IOException {
        synchronized(session){
            session.getAsyncRemote().sendText(message);
        }
        //this.session.getAsyncRemote().sendText(message);
    }

    /**
     * 向客户端发送人数消息
     */
    public void sendOnOpenVo(OnOpenVo onOpenVo) throws IOException, EncodeException {
        this.session.getBasicRemote().sendObject(onOpenVo);
        //this.session.getAsyncRemote().sendText(message);
    }

    /**
     * 通过userId向客户端发送消息
     */
    public void sendMessageByUserId(String userId, String message) throws IOException {
        logger.info("服务端发送消息到{},消息：{}",userId,message);
        if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
            webSocketMap.get(userId).sendMessage(message);
        }else{
            logger.error("用户{}不在线",userId);
        }

    }

    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message) throws IOException {
        for (Integer item : webSocketMap.keySet()) {
            try {
                webSocketMap.get(item).sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

}
